#ifndef RTSP2012_LOG_H
#define RTSP2012_LOG_H

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <sys/time.h>

#include <sstream>
#include <iomanip>
#include <string>

namespace rtsp2012 {
namespace log {

enum Level
{
	LOG_DEBUG	,
	LOG_INFO	,
	LOG_WARN	,
	LOG_ERROR	,
};

typedef const char * tag_t;

class LogObj
{
public:
	inline LogObj(Level l, tag_t tag)
		: level_(l), tag_(tag)
	{
		timeval tv;
		memset(&tv, 0, sizeof(tv));
		gettimeofday(&tv, 0);
		ss << ::std::setfill('0') << ::std::setw(6) << tv.tv_usec;
		ss << tag_;
	}

	inline LogObj(LogObj & log)
		: level_(log.level_), tag_(log.tag_)
	{
		ss << log.ss;
	}

	~LogObj();

private:
	Level const level_;
	tag_t tag_;
	::std::stringstream ss;

public:
#define __ return ((ss << arg), *this);

	template <typename T> inline LogObj & operator << (const T & arg) { __ }
	template <typename T> inline LogObj & operator << (const T * arg) { __ }

	typedef ::std::ostream ostream;
	typedef ::std::ios ios;
	typedef ::std::ios_base ios_base;

	inline LogObj & operator << (ostream& (*arg)(ostream&)) { __ }
	inline LogObj & operator << (ios& (*arg)(ios&)) { __ }
	inline LogObj & operator << (ios_base& (*arg)(ios_base&)) { __ }

#undef __
};

class LogObjSeed
{
public:
	inline LogObjSeed(Level l, tag_t tag)
		: level_(l), tag_(tag), tag_len_(strlen(tag))
	{}

	inline Level level() const { return level_; }
	inline tag_t tag() const { return tag_; }

private:
	Level const level_;
	tag_t tag_;
	int tag_len_;

public:
#define __ LogObj log(level_, tag_); log << arg; return log;

	template <typename T> inline LogObj operator << (const T & arg) { __ }
	template <typename T> inline LogObj operator << (const T * arg) { __ }

	typedef ::std::ostream ostream;
	typedef ::std::ios ios;
	typedef ::std::ios_base ios_base;

	inline LogObj operator << (ostream& (*arg)(ostream&)) { __ }
	inline LogObj operator << (ios& (*arg)(ios&)) { __ }
	inline LogObj operator << (ios_base& (*arg)(ios_base&)) { __ }

#undef __

public:
	template <typename T>
	inline void operator () (const T & arg)
	{
		LogObj log(level_, tag_);
		log << arg;
	}

	template <typename T>
	inline void operator () (const T * arg)
	{
		LogObj log(level_, tag_);
		log << arg;
	}

	void operator () (const char * format, ...);

};

class Logger
{
private:
	const char * const name_;

public:
	LogObjSeed error;
	LogObjSeed warn;
	LogObjSeed info;
	LogObjSeed debug;

	Logger(tag_t name,
			tag_t tag_error,
			tag_t tag_warn,
			tag_t tag_info,
			tag_t tag_debug
		)
		: name_(name),
		error(LOG_ERROR, tag_error),
		warn(LOG_WARN, tag_warn),
		info(LOG_INFO, tag_info),
		debug(LOG_DEBUG, tag_debug)
	{}

	const char * name() const { return name_; }
};

} /* namespace log */
} /* namespace rtsp2012 */


#define PREFIX_ERROR "[ERROR] "
#define PREFIX_WARN "[WARN] "
#define PREFIX_INFO "[info] "
#define PREFIX_DEBUG "[debug] "

#define LOG2012_AUTO(log_name) \
	using namespace ::rtsp2012; \
	using namespace ::rtsp2012::log; \
	::rtsp2012::log::Logger( \
		#log_name, \
		PREFIX_ERROR #log_name " -- ", \
		PREFIX_WARN  #log_name " -- ", \
		PREFIX_INFO  #log_name " -- ", \
		PREFIX_DEBUG #log_name " -- " \
	);

#define LOG2012(log_name) \
	namespace { LOG2012_AUTO(log_name) }


#endif /* RTSP2012_LOG_H */

